﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Windows.Forms.DataVisualization;

using SpectationClient.DGVExtensions;
using SpectationClient.Stuff;
using SpectationClient.SpectralTools;

namespace SpectationClient.GUI.UC {


    public partial class UC_SpectralViewer : UserControl {
        private Random random = new Random(42);
        public UInt32 maxNumerOfSeries { get; set; }
        private struct linkedColumnInfo{

            private DataGridView datagridView;
            public DataGridView DGV{
                get{return this.datagridView;}
            }

            private Int32 indexSpecColumn;
            public Int32 IndexSpecColumn{get{return this.indexSpecColumn;}}

            private Int32 indexColorColumn;
            public Int32 IndexColorColumn{get{return this.indexColorColumn;}}

            private DataGridViewSpectrumColumn specColumn;
            public DataGridViewSpectrumColumn SpecColumn {get{return this.specColumn;}}

            private DataGridViewColorColumn colorColumn;
            public DataGridViewColorColumn ColorColumn {get{return this.colorColumn;} }

            private Dictionary<Int32, Series> lut;
            public Dictionary<Int32, Series> LUT {
                get{return this.lut;} 
                set{this.lut = value;}
            }

            

            public linkedColumnInfo(DataGridViewSpectrumColumn specColumn, DataGridViewColorColumn colorColumn=null) {
                this.datagridView = specColumn.DataGridView;    
                this.indexSpecColumn = specColumn.Index;
                this.indexColorColumn = -1;

                this.colorColumn = null;
                if(colorColumn != null){
                    this.colorColumn = colorColumn;
                    this.indexColorColumn = colorColumn.Index;
                }
                this.specColumn = specColumn;
                this.lut = new Dictionary<int, Series>();
            }
        }


        public String LegendTitle {
            get { return this.Chart.Legends[0].Title; }
            set { this.Chart.Legends[0].Title = value; }
        }

        private List<linkedColumnInfo> linkedColumns = new List<linkedColumnInfo>();
       
        public UC_SpectralViewer() {
            InitializeComponent();
            init();    
        }

        private void init() {
            this.Disposed +=new EventHandler(UC_SpectralViewer_Disposed);
            this.maxNumerOfSeries = Properties.Settings.Default.MAX_CHARTSERIES ;
            
        }

      
        void UC_SpectralViewer_Disposed(object sender, EventArgs e) {
            foreach(var t in this.linkedColumns) {
               t.SpecColumn.DataGridView.SelectionChanged -= DGV_SelectionChanged;
            }
        }

        
        public void linkToSpectrumColumn(DataGridViewSpectrumColumn spectrumColumn, DataGridViewColorColumn colorColumn=null) {
            spectrumColumn.DataGridView.SelectionChanged +=new EventHandler(DGV_SelectionChanged);
            
            if(colorColumn != null){
                if(spectrumColumn.DataGridView != colorColumn.DataGridView)
                    throw new Exception("spectrumColumn and colorColumn must be from same DataGridView");
            }
            linkedColumns.Add(new linkedColumnInfo(spectrumColumn, colorColumn));
            
        }

        
        public void RefreshPlot(){
            List<DataGridView> activated = new List<DataGridView>();
            foreach(linkedColumnInfo li in this.linkedColumns) {

                if(li.DGV != null && !activated.Contains(li.DGV)) this.DGV_SelectionChanged(li.DGV, null);
            
            }
            
        }

        void DGV_SelectionChanged(object sender, EventArgs e) {

         

            //identify relevant linkedColumnInfos
            DataGridView DGV = sender as DataGridView;
            List<linkedColumnInfo> linkedColumnInfos = null;
            if(DGV != null){
                linkedColumnInfos = (from x in this.linkedColumns 
                        where x.SpecColumn.DataGridView == DGV
                        select x).ToList();
            }else{
                linkedColumnInfos = this.linkedColumns;
            }
            int nLinked = this.linkedColumns.Count;
           

            foreach(linkedColumnInfo lc in linkedColumnInfos){
                DGV = lc.DGV;
                int cS = lc.IndexSpecColumn;
                int cC = lc.IndexColorColumn;
                bool useColorCell = cC != -1;

                List<int> selectedRows = (from DataGridViewRow row in DGV.Rows
                                          where row.Cells[cS].Selected
                                          select row.Index).ToList();



                Dictionary<Int32, Series> LUT = lc.LUT;
                
                List<int> toRemove = LUT.Keys.Except(selectedRows).ToList();
                
                //remove unselected spectra
                foreach(int r in toRemove) {
                    Chart.Series.Remove(LUT[r]);
                    LUT.Remove(r);
                }

                //add new selected spectra
                List<int> toAdd = selectedRows.Except(LUT.Keys).ToList();
                
                foreach(int r in toAdd){
                    if(Chart.Series.Count >= this.maxNumerOfSeries) break;
                    Spectrum spec = ((DataGridViewSpectrumCell)DGV[cS,r]).getSpectrum();
                    if(spec == null) continue;


                    String name = nLinked == 1 ? String.Format("    {0} {1}", r+1, spec.SpectrumName) :
                                                 String.Format("{0}:{1} {2}", cS+1,r+1, spec.SpectrumName);
                    Color color = useColorCell ? ((DataGridViewColorCell)DGV[cC,r]).getColor() : getColor();

                    Series series = getSpectrumPointSeries(spec,false);
                    series.Name = name;
                    series.YValueType = ChartValueType.Single;
                    series.XValueType = ChartValueType.Single;
                    
                    series.Tag = spec;

                    series.Color = color;
                    series.ChartType = SeriesChartType.Line;
                    LUT.Add(r, series);
                    Chart.Series.Add(series);
                }
            
            }
        }

        private Series getSpectrumPointSeries(Spectrum spec, bool SortByBands) {
            Series series = new Series(spec.SpectrumName);
            bool bt = (spec.originalASD_BLOB == null);
            for(int i = 0; i < spec.BandValues.Length; i++) {
                DataPoint point = new DataPoint(spec.Bands[i], spec.BandValues[i]);
                point.IsVisibleInLegend = true;
                point.ToolTip = String.Format("x:{0}\ny:{1}", point.XValue, point.YValues[0]);
                series.Points.Add(point);
            }

            if(spec.BadBands != null) {
                for(int i = 0; i < spec.BandValues.Length; i++) {
                    series.Points[i].IsEmpty = !spec.BadBands[i];
                }
            }


            return series;
        }

        private Color getColor() {
            Color x = Color.ForestGreen;
            return Color.FromArgb(255,
                this.random.Next(0, 255),
                this.random.Next(0, 255),
                this.random.Next(0, 255)
                );

        }


        private void menuItem_YRange_01_Click(object sender, EventArgs e) {
            ChartArea CA = this.Chart.ChartAreas[0];
            SeriesCollection S = this.Chart.Series;

            CA.AxisY.Minimum = -0.1;
            CA.AxisY.Maximum = 1.1;
            CA.AxisY.ScaleView.Zoom(CA.AxisY.Minimum, CA.AxisY.Maximum);
            CA.AxisX.ScaleView.Zoom(CA.AxisX.Minimum, CA.AxisX.Maximum);
            CA.AxisX.RoundAxisValues();
            CA.AxisY.RoundAxisValues();
        }

        private void menuItem_YRangeInf_Click(object sender, EventArgs e) {
            ChartArea CA = this.Chart.ChartAreas[0];
            SeriesCollection S = this.Chart.Series;

            if(S.Count > 0) {
                Double minY = 0.0;
                Double maxY = 0.0001;
                for(int i = 0; i < S.Count; i++) {
                    if(S[i].Points.Count > 0) {
                        DataPoint Pmin = S[i].Points.FindMinByValue();
                        DataPoint Pmax = S[i].Points.FindMaxByValue();
                        if(Pmin != null) minY = Math.Min(minY, Pmin.YValues.Min());
                        if(Pmax != null) maxY = Math.Max(maxY, Pmax.YValues.Max());
                    }
                }
                Double range = maxY - minY;
                CA.AxisY.Minimum = minY - range*0.05;
                CA.AxisY.Maximum = maxY + range*0.05;
                CA.AxisY.ScaleView.Zoom(CA.AxisY.Minimum, CA.AxisY.Maximum);
                CA.AxisX.ScaleView.Zoom(CA.AxisX.Minimum, CA.AxisX.Maximum);
                CA.AxisX.RoundAxisValues();
                CA.AxisY.RoundAxisValues();
            }
           
        }

        private void menuItem_SaveAsImage_Click(object sender, EventArgs e) {
            ChartArea CA = this.Chart.ChartAreas[0];
         
            SVD.Filter = DataHelper.ImageFormatFilter;
            SVD.AddExtension = true;
            SVD.FileName = "Chart";
            if(SVD.ShowDialog() == DialogResult.OK){
                Chart.SaveImage(SVD.FileName, DataHelper.parseImageFormat(SVD.FileName));
            }
        }

        private void Chart_GetToolTipText_1(object sender, ToolTipEventArgs e) {
            switch(e.HitTestResult.ChartElementType) {
                case ChartElementType.Axis:
                case ChartElementType.TickMarks:
                    HitTestResult result = Chart.HitTest(e.X, e.Y, ChartElementType.DataPoint);
                    if(result.ChartElementType == ChartElementType.DataPoint) {
                        e.Text = result.Series.Points[result.PointIndex].ToolTip;
                    }
                    break;
                case ChartElementType.DataPoint:
                    HitTestResult result2 = Chart.HitTest(e.X, e.Y, ChartElementType.DataPoint);
                    if(result2.ChartElementType == ChartElementType.DataPoint) {
                        e.Text = result2.Series.Points[result2.PointIndex].ToolTip;
                    }
                    break;
                default:
                    break;
            }
        }

        private void tsmi_saveASD_Click(object sender, EventArgs e) {

            FormHelper.saveSpectraAsASD(this.getSpectraFromSeries(), this.getSpectraNamesFromSeries());
        }

        private void tsmi_saveESL_Click(object sender, EventArgs e) {
            FormHelper.saveSpectraAsESL(this.getSpectraFromSeries());
        }

        private void tsmi_saveClipboard_Click(object sender, EventArgs e) {
            MemoryStream ms = new MemoryStream();
            Stream s = ms;
            this.Chart.SaveImage(ms, ChartImageFormat.Bmp);
            Image img = new Bitmap(ms);
            Clipboard.SetImage(img);
            s.Close();
        }

        private List<Spectrum> getSpectraFromSeries() {
            
            return (from Series s in this.Chart.Series
                    where s.Tag is Spectrum
                    select s.Tag as Spectrum).ToList();

        }


        private List<String> getSpectraNamesFromSeries() {

            return (from Series s in this.Chart.Series
                    where s.Tag is Spectrum
                    select s.Name).ToList();

        }

        private void tsmi_spectraToClipboard_Click(object sender, EventArgs e) {

            Char sep = '\t';
            var counts = (from Series s in this.Chart.Series
                     select s.Points.Count).ToArray();
            int maxCount = counts.Max();
            StringBuilder SB = new StringBuilder();

            //write header
            for(int iSeries = 0; iSeries < Chart.Series.Count; iSeries++) {
                SB.Append(Chart.Series[iSeries].Name + sep);
            }
            SB.Append("\n");

            //write rest
            for(int iPoint = 0; iPoint < maxCount; iPoint++) {
                for(int iSeries = 0; iSeries < Chart.Series.Count; iSeries++) {
                    if(counts[iSeries] > iPoint) {
                        SB.Append(Chart.Series[iSeries].Points[iPoint].YValues[0]);
                    }   
                    SB.Append(sep);
                }
                SB.Append("\n");
            }


            Clipboard.SetData(DataFormats.Text, SB.ToString());


        }

     
    }
}
